package com.bq.server.configuration;

import com.bq.server.constant.DateFormatterConstant;
import com.bq.server.enumeration.Enumeration;
import com.bq.server.enumeration.EnumerationCache;
import com.bq.server.enumeration.ErrorCodeEnum;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;


/**
 * jackson 配置
 * @author: ChenSH
 * @date: Create by in 15:29 2019-09-20
 */
@Configuration
public class Jackson2Configuration implements DateFormatterConstant {

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer tradeJackson2ObjectMapperBuilderCustomizer() {
        return builder -> {

            JavaTimeModule javaTimeModule = new JavaTimeModule();
            //LocalDate
            javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DEFAULT_DATE_FORMATTER));
            javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DEFAULT_DATE_FORMATTER));
            //LocalTime
            javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DEFAULT_TIME_FORMATTER));
            javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DEFAULT_TIME_FORMATTER));
            //LocalDateTime
            javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DEFAULT_DATE_TIME_FORMATTER));
            javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DEFAULT_DATE_TIME_FORMATTER));

            SimpleModule simpleModule = new SimpleModule();
            //枚举反序列化
            simpleModule.addDeserializer(Enum.class, new EnumerationDeserializer());
            builder.dateFormat(new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT))
                    .failOnUnknownProperties(false)
                    .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                    //null值不序列化
                    .serializationInclusion(JsonInclude.Include.NON_NULL)
                    .modules(javaTimeModule, simpleModule);
        };
    }

    public static class EnumerationDeserializer extends JsonDeserializer<Enum> implements ContextualDeserializer {

        private Class<? extends Enumeration> enumClass;

        @Override
        public Enum deserialize(JsonParser jsonParser, DeserializationContext ctx) throws IOException {
            if (StringUtils.isEmpty(jsonParser.getText())) {
                return null;
            }
            Enum anEnum = (Enum) EnumerationCache.valueOf(enumClass, jsonParser.getValueAsInt());
            if (anEnum == null) {
                throw ErrorCodeEnum.ERROR_CLIENT_ENUM_NOT_FOUND
                        .businessException(jsonParser.currentName()
                                        + "="
                                        + jsonParser.getIntValue(),
                                EnumerationCache.getEnumValues(enumClass).keySet());
            }
            return anEnum;
        }

        @Override
        @SuppressWarnings("unchecked")
        public JsonDeserializer<?> createContextual(DeserializationContext ctx, BeanProperty property) {
            JavaType javaType = ctx.getContextualType();
            EnumerationDeserializer clone = new EnumerationDeserializer();
            if (javaType.isTypeOrSubTypeOf(Enumeration.class)) {
                clone.enumClass = (Class<? extends Enumeration>) javaType.getRawClass();
            } else {
                throw ErrorCodeEnum.ERROR_NOT_IMPLEMENTED_ENUMERATION.businessException(javaType.getRawClass().getName());
            }
            return clone;


        }


    }
}
